#include "game_rms.h"
#include "game_application.h"

#include "lang/lang.h"
#include "lang/rules.h"
#include "script/scripthelper.h"

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif//_ENABLE_LUABIND

#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

using namespace util;

GameRMS::GameRMS() :
	m_IsLoaded(false)
{
	std::string		Path = g_pApp->GetHandler()->GetSaveFolder();
	m_Filename = Path+g_pApp->GetSaveFile();
	//Load();
}

GameRMS::~GameRMS()
{
	//if (m_IsLoaded)	Save();
}

GameRMS*	GameRMS::Instance()
{
	if (g_pApp)
		return g_pApp->GetRMS();
	return NULL;
}

void		GameRMS::Load()
{
	util::FileIO	File;
	if (!File.Open(m_Filename, FileIO::FILE_READ|FileIO::FILE_BINARY))
	{
		// doesn't exist.
		// create it, fill with default values, then save
		Reset();
		Save();
		m_IsLoaded = true;
		return;
	}
	m_IsLoaded = true;
	File.Read(&m_Data, sizeof(m_Data));
	if (m_Data.VersionNum != GetVersion() || m_Data.MagicNum != GetMagic())
	{
		_LOG(MSG_ERROR, "Unable to load RMS.. invalid version# or Magic#");
		Reset();
		Save();
		return;
	}
	LoadCustom(&File);
}

void		GameRMS::Save()
{
	util::FileIO	File;
	if (File.Open(m_Filename, FileIO::FILE_WRITE|FileIO::FILE_BINARY))
	{
		File.Write(&m_Data, sizeof(m_Data));
		SaveCustom(&File);
	}
}

void		GameRMS::Reset()
{
	memset(&m_Data,0 , sizeof(m_Data));

	m_Data.MagicNum		= GetMagic();
	m_Data.VersionNum	= GetVersion();

	m_Data.UseFullscreen = false;
	m_Data.MusicVolume = DefaultMusicVol();
	m_Data.SoundVolume = DefaultSoundVol();
	strcpy(m_Data.Language, "");
	for (int j=0; j<MAX_PROFILES; j++)
	{
		for (int i=0; i<LEVEL_C; i++)
		{
			m_Data.Profiles[j].LevelA[i].HighestRank = 10000;
			m_Data.Profiles[j].LevelA[i].Score = 0;
			m_Data.Profiles[j].LevelA[i].State =0;
		}
		for (int i=0; i<CONST_C; i++)
		{
			m_Data.Profiles[j].ConstantA[i] = 0;
		}

		for (int i=0; i<TROPHY_C; i++)
		{
			m_Data.Profiles[j].TrophyA[i] = 0;
		}
	}


	ResetCustom();
}

bool		GameRMS::IsCheating()
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return false;
	return pProfile->Cheating;
}

void		GameRMS::SetInt(int Index, int Value)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return;
	if (Index < 0 || Index >=CONST_C) return ;
	pProfile->ConstantA[Index] = Value;
}

int			GameRMS::GetInt(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return 0;
	if (Index < 0 || Index >=CONST_C) 
		return 0;
	return pProfile->ConstantA[Index];
}

int			GameRMS::IsOpen(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return 0;
	if (Index < 0 || Index >=LEVEL_C) return false;
	return pProfile->LevelA[Index].State;
}

bool		GameRMS::IsCleared(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return false;
	if (Index < 0 || Index >=LEVEL_C) return false;
	return pProfile->LevelA[Index].State > 1;
}

void		GameRMS::SetOpen(int Index, int Val)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return;
	if (Index < 0 || Index >=LEVEL_C) return;
	pProfile->LevelA[Index].State = Val;
}

int				GameRMS::GetRank(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return 0;
	if (Index < 0 || Index >=LEVEL_C) return 100;
	return pProfile->LevelA[Index].HighestRank;
}

int				GameRMS::GetScore(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return 0;
	if (Index < 0 || Index >=LEVEL_C) return 0;
	return pProfile->LevelA[Index].Score;
}

void				GameRMS::SetRank(int Index, int Rank)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return;
	if (Index < 0 || Index >=LEVEL_C) return;
	pProfile->LevelA[Index].HighestRank = Rank;
}

void				GameRMS::SetScore(int Index, int Score)
{
	Profile*	pProfile = GetProfile();	
	if (!pProfile) return;
	if (Index < 0 || Index >=LEVEL_C) return;
	pProfile->LevelA[Index].Score = Score;
}

bool				GameRMS::AddProfile(const std::string& Name, const std::string& Language, const std::string& Location)
{
	if (GetProfileC() >= GetMaxProfileC()) 
	{
		_LOG(MSG_WARNING, "Unable to add a profile.. delete one first!");
		return false;	// no space
	}
	m_Data.CurrentProfile = m_Data.ProfileC;
	Profile* pProfile = &m_Data.Profiles[m_Data.CurrentProfile];
	memset(pProfile, 0, sizeof(Profile));	// reset the space
	ClearCustomProfile(m_Data.CurrentProfile); // custom reset

	if (Name.size() > NAME_LENGTH) return false;
	if (Location.size() > NAME_LENGTH) return false;

	if (strcmpi(Name.c_str(), g_pApp->GetCheater().c_str())==0)
	{
		pProfile->Cheating = true;
	}

	strcpy(pProfile->Name, Name.c_str());
	strcpy(pProfile->Language, Language.c_str());
	strcpy(pProfile->Location, Location.c_str());
	
	_LOG(MSG_COMMENT, "Adding profile " << Name << " [ "<< Language << "] to index " << m_Data.ProfileC);
	m_Data.ProfileC++;
	return true;
}
void				GameRMS::SetProfile(int ID)
{
	if (ID >= 0 && ID < GetProfileC())
	{
		m_Data.CurrentProfile = ID;
		_LOG(MSG_COMMENT, "Setting profile " << ID);
	} else
	{
		_LOG(MSG_WARNING, "Invalid profile ID " << ID);
	}
}
void				GameRMS::DeleteProfile(int ID)
{
	if (ID < 0 || ID >= GetProfileC())
	{
		_LOG(MSG_WARNING, "Invalid profile ID " << ID);
		return;
	}
	memset(&m_Data.Profiles[ID], 0, sizeof(Profile));	// reset the space
	for (int i=ID; i<GetProfileC(); i++)
	{
		memcpy(&m_Data.Profiles[i], &m_Data.Profiles[i+1], sizeof(Profile));
	}

	DeleteCustomProfile(ID);

	m_Data.ProfileC --;
	_LOG(MSG_COMMENT, "Deleting profile " << ID << ", " <<  m_Data.ProfileC << " profiles left");
	if (m_Data.CurrentProfile >= ID) m_Data.CurrentProfile--;
}

static const char* INVALID = "";
const char*			GameRMS::GetName()
{
	Profile*	pProfile = GetProfile();	
	if (pProfile)	return pProfile->Name; else
					return INVALID;
}
const char*			GameRMS::GetNameFromID(int ID)
{
	if (ID >= 0 && ID < GetProfileC())
	{
		return	m_Data.Profiles[ID].Name;
	} else	return INVALID;
}
const char*			GameRMS::GetLanguage()
{
	return m_Data.Language;
}

void				GameRMS::SetLanguage(const std::string& Name)
{
	std::string	Temp = Name;
	if (Temp.size() >= NAME_LENGTH)	Temp.resize(NAME_LENGTH-1);
	strcpy(m_Data.Language, Temp.c_str());

	lang::Rules::Instance().SetLanguage(Name);
}

const char*			GameRMS::GetLanguageFromID(int ID)
{
	return m_Data.Language;
}

const char*			GameRMS::GetLocation()
{
	static const char*	Default = "english";
	Profile*	pProfile = GetProfile();	
	if (pProfile)	return pProfile->Location; else
					return Default;
}
const char*			GameRMS::GetLocationFromID(int ID)
{
	if (ID >= 0 && ID < GetProfileC())
	{
		return	m_Data.Profiles[ID].Location;
	} else	return INVALID;
}

int					GameRMS::GetTrophyValue(int Index)
{
	Profile*	pProfile = GetProfile();	
	if (pProfile && Index >= 0 && Index < TROPHY_C)	
		return pProfile->TrophyA[Index]; 
	else
		return -1;
}

void				GameRMS::SetTrophyValue(int Index, int Value)
{
	Profile*	pProfile = GetProfile();	
	if (pProfile && Index >= 0 && Index < TROPHY_C)	
		pProfile->TrophyA[Index] = Value; 
}

GameRMS::Profile*			GameRMS::GetProfile()
{
	if (!m_IsLoaded) return NULL;
	if (m_Data.CurrentProfile >= 0 && m_Data.CurrentProfile < GetProfileC())
		return &m_Data.Profiles[m_Data.CurrentProfile];
	return NULL;
}

bool		GameRMS::IsValidProfileNameA(const std::string& Name)
{
	if (Name.empty()) return false;
	if (Name.size() >= (NAME_LENGTH - 1)) return false;

	bool IsAllSpace = true;
	unsigned char ch;
	for (int i = 0; i < (int)Name.size(); i++)
	{
		ch = Name.c_str()[i]; // ensure is ASCII
		if ((ch < 255) && !isspace(ch))
		{
			IsAllSpace = false;
			break;
		}
	}
	if (IsAllSpace) return false;

	return true;
}

bool		GameRMS::IsValidProfileNameW(const std::wstring& Name)
{
	// RMS only supports ASCII names?
	return IsValidProfileNameA( lang::Lang::ConvertWtoA(Name) );
}

bool		GameRMS::ProfileNameExistsA(const std::string& Name, bool CaseSensitive)
{
	int ProfileC = GetProfileC();
	for (int i = 0; i < ProfileC; i++)
	{
		const char* szName = GetNameFromID(i);
		if (CaseSensitive)
		{
			if (0 == strcmp(szName, Name.c_str()))
				return true;
		}
		else
		{
			if (0 == stricmp(szName, Name.c_str()))
				return true;
		}
	}
	return false;
}

bool		GameRMS::ProfileNameExistsW(const std::wstring& Name, bool CaseSensitive)
{
	// RMS only supports ASCII names?
	return ProfileNameExistsA( lang::Lang::ConvertWtoA(Name), CaseSensitive );
}

void		GameRMS::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
    lua_State * L = _pScript->GetCState ( );

	luabind::module(L)
    [
    luabind::class_<GameRMS>( "GameRMS" )
		.def("Load",					&GameRMS::Load)
		 .def("Save",					&GameRMS::Save)
		.def("Reset",					&GameRMS::Reset)
		.def("IsUseFullscreenEnabled",  &GameRMS::IsUseFullscreenEnabled)
		.def("SetUseFullscreenEnabled", &GameRMS::SetUseFullscreenEnabled)
		.def("GetSoundVolume",			&GameRMS::GetSoundVolume)
		.def("GetMusicVolume",			&GameRMS::GetMusicVolume)
		.def("SetSoundVolume",			&GameRMS::SetSoundVolume)
		.def("SetMusicVolume",			&GameRMS::SetMusicVolume)

		.def("IsOpen",					&GameRMS::IsOpen)
		.def("IsCleared",				&GameRMS::IsCleared)
		.def("SetOpen",					&GameRMS::SetOpen)

		.def("IsCheating",				&GameRMS::IsCheating)
		.def("GetRank",					&GameRMS::GetRank)
		.def("GetScore",				&GameRMS::GetScore)
		.def("SetRank",					&GameRMS::SetRank)
		.def("SetScore",				&GameRMS::SetScore)

		.def("GetTrophyValue",			&GameRMS::GetTrophyValue)
		.def("SetTrophyValue",			&GameRMS::SetTrophyValue)

		.def("AddProfile",				&GameRMS::AddProfile)
		.def("GetProfileC",				&GameRMS::GetProfileC)
		.def("GetCurrentProfile",		&GameRMS::GetCurrentProfile)
		
		.def("GetMaxProfileC",			&GameRMS::GetMaxProfileC)
		.def("ProfileValid",			&GameRMS::ProfileValid)
		.def("SetProfile",				&GameRMS::SetProfile)
		.def("DeleteProfile",			&GameRMS::DeleteProfile)
		.def("GetName",					&GameRMS::GetName)
		.def("GetNameFromID",			&GameRMS::GetNameFromID)
		.def("SetLanguage",				&GameRMS::SetLanguage)
		.def("GetLanguage",				&GameRMS::GetLanguage)
		.def("GetLanguageFromID",		&GameRMS::GetLanguageFromID)
		.def("GetLocation",				&GameRMS::GetLocation)
		.def("GetLocationFromID",		&GameRMS::GetLocationFromID)
		.def("IsValidProfileName",		&GameRMS::IsValidProfileNameA)
		.def("ProfileNameExists",		&GameRMS::ProfileNameExistsA)

		.def("GetInt",					&GameRMS::GetInt)
		.def("SetInt",					&GameRMS::SetInt)
	];
    
	luabind::module(L)
    [	luabind::def("GetRMS",			 &GameRMS::Instance ,  luabind::detail::null_type())	 ];
	
	RegisterCustom(_pScript);	// register any custom stuff as children
	
#endif //_ENABLE_LUABIND
}

